# -*- coding: utf-8 -*-   p105
from sklearn.feature_extraction import DictVectorizer
import csv
from sklearn import tree
from sklearn import preprocessing
import graphviz

# 将特征名称放入字典中
activedata = open('./data_activity.csv', 'r')
reader = csv.reader(activedata)
headers = next(reader)
# 将各样本的特征以字典形式放入列表，将各样本的类别放入列表
FeatureList = []  # 创建一个空列表，用于存储特征数据（每行除标签外的数据）
Labellist = []  # 创建一个空列表，用于存储标签数据（每行的最后一列数据）
for row in reader:  # 遍历CSV读取器对象中的每一行
    # 将当前行的最后一列（标签）添加到Labellist列表中
    Labellist.append(row[len(row) - 1])
    rowDict = {}  # 创建一个空字典，用于存储当前行的特征数据
    # 遍历当前行的每一列（除了最后一列，即标签）
    for i in range(len(row) - 1):
        # 将当前列的值（row[i]）和对应的列标题（headers[i]）作为键值对添加到rowDict字典中
        rowDict[headers[i]] = row[i]
        # 将包含当前行特征数据的字典添加到FeatureList列表中
    FeatureList.append(rowDict)
# 将特征、标签的字符串取值转换为数值    将字典列表或类似的结构 转换为NumPy数组或稀疏矩阵
'''
vec = DictVectorizer() 创建了一个DictVectorizer的实例。这个实例将用于将特征数据（字典列表）转换为NumPy数组。
vec.fit_transform(FeatureList) 对FeatureList进行了拟合和转换。fit_transform方法首先学习FeatureList中所有字典的键（即特征名称），然后将这些字典转换为一个稀疏矩阵，
其中每行对应FeatureList中的一个字典，每列对应一个唯一的特征。如果某个字典中缺少某个特征，则在相应的位置填充0。
.toarray() 方法将稀疏矩阵转换为标准的NumPy数组。这通常是为了方便后续处理，因为NumPy数组在大多数数值计算库和Python科学计算生态系统中都是通用的。
dummyX 是转换后的NumPy数组，其中包含了转换后的特征数据。dummyX的每一行对应原始FeatureList中的一个字典（即一个样本），每一列对应一个特征。
'''
vec = DictVectorizer()
dummyX = vec.fit_transform(FeatureList).toarray()

print(vec.get_feature_names_out())
print("dummyX: " + str(dummyX))        # 从打印结果可以看出，第一列是湿度高、第二列是湿度正常

lb = preprocessing.LabelBinarizer()            #  将标签特征二元化
dummyY = lb.fit_transform(Labellist)
print("dummyY: " + str(dummyY))

# 训练模型
clf = tree.DecisionTreeClassifier(criterion='entropy')
clf = clf.fit(dummyX, dummyY)

# 可视化决策树
# with open('./img/ActivityID3Tree.dot', 'w') as f:
#     f = tree.export_graphviz(clf, feature_names=vec.get_feature_names(), out_file=f)
dot_data = tree.export_graphviz(clf, out_file=None,
                         filled=True, rounded=True, feature_names=vec.get_feature_names_out(),
                         special_characters=True)
graph = graphviz.Source(dot_data)                  # graphviz windows需要安装，将bin目录 添加到path。
graph.render("ActivityID3Tree.dot", "./img/", view=True)

# 预测
OneRowX = dummyX[0, :]  # 选择了dummyX数组的第一行（索引为0的行），即第一个样本的所有特征
newRowX = OneRowX       # 创建了一个与OneRowX相同的NumPy数组（实际上是OneRowX的一个副本）
 # 修改newRowX数组中的特定元素（即特征值）
newRowX[0] = 1          # 将第一个特征的值设置为1
newRowX[2] = 0          # 将第三个特征的值设置为0
 # 注意：这里的修改实际上只影响了newRowX数组，而没有改变OneRowX或dummyX数组
 # 由于大多数机器学习模型的predict方法期望输入是一个二维数组（即使只有一个样本）
# 因此，我们需要将newRowX（现在是一个一维数组）转换为一个二维数组
newRowX = [newRowX]     # 将newRowX转换为一个只包含一个数组的列表，这个数组本身是一个一维数组
# 使用训练好的分类器clf对修改后的样本进行预测
predict = clf.predict(newRowX)  # clf是一个已经训练好的分类器实例
print ("newRowX:" + str(predict))
